package service

/**
 * Author: 孙克旭_泺栋
 * Description: deployments业务处理
 * Date: 2025-09-27
 */

import (
	"context"
	"encoding/json"
	"kubernate-server/conf"
	"net/http"

	"github.com/gin-gonic/gin"
	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
)

type DeploymentsService struct{}

/*
DeploymentCreate
创建 Deployment 的请求结构体，用于接收前端请求体中的数据参数
*/
type DeploymentCreate struct {
	Name          string            `json:"name" binding:"required"`
	Namespace     string            `json:"namespace" binding:"required"`
	Replicas      int32             `json:"replicas" binding:"required,min=1"`
	Image         string            `json:"image" binding:"required"`
	ContainerName string            `json:"containerName" binding:"required"`
	Ports         []ContainerPort   `json:"ports,omitempty"`
	Labels        map[string]string `json:"labels,omitempty"`
	Env           []EnvVar          `json:"env,omitempty"`
}

// DeploymentUpdate 更新 Deployment 的请求结构体
type DeploymentUpdate struct {
	Name      string  `json:"name" binding:"required"`
	Namespace string  `json:"namespace" binding:"required"`
	Replicas  *int32  `json:"replicas,omitempty"`
	Image     *string `json:"image,omitempty"`
}

// DeploymentInfo Deployment 信息响应结构体
type DeploymentInfo struct {
	Name              string            `json:"name"`
	Namespace         string            `json:"namespace"`
	Replicas          int32             `json:"replicas"`
	AvailableReplicas int32             `json:"availableReplicas"` //可用副本数
	ReadyReplicas     int32             `json:"readyReplicas"`     //就绪副本数
	Image             []string          `json:"images"`            // 所有容器镜像
	Labels            map[string]string `json:"labels"`
	CreationTimestamp metav1.Time       `json:"creationTimestamp"`
}

// ContainerPort 容器端口配置
type ContainerPort struct {
	Name          string `json:"name,omitempty"`
	ContainerPort int32  `json:"containerPort" binding:"required"`
}

// EnvVar 环境变量
type EnvVar struct {
	Name  string `json:"name" binding:"required"`
	Value string `json:"value" binding:"required"`
}

/*
CreateDeploy 创建 Deployment
仅考虑deploy定义一个容器的情况
*/
func (DeploymentsService) CreateDeploy(ctx *gin.Context) {
	// todo 1. 创建接收前端请求参数的对象，并将请求体携带的参数与对象的字段进行绑定
	// 1.1 定义初始化构建deploy对象
	var deployReq DeploymentCreate

	// 1.2 绑定请求参数
	if err := ctx.ShouldBindJSON(&deployReq); err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// todo 2. 将自定义的deploy对象赋值给官方定义的deployment对象
	// 2.1 构建容器端口
	var containerPorts []corev1.ContainerPort
	for _, port := range deployReq.Ports {
		containerPorts = append(containerPorts, corev1.ContainerPort{
			Name:          port.Name,
			ContainerPort: port.ContainerPort,
		})
	}

	// 2.2 构建环境变量
	var envVars []corev1.EnvVar
	for _, env := range deployReq.Env {
		envVars = append(envVars, corev1.EnvVar{
			Name:  env.Name,
			Value: env.Value,
		})
	}

	// 2.3 定义 Deployment资源
	deployment := &appsv1.Deployment{
		ObjectMeta: metav1.ObjectMeta{ // 元数据
			Name:      deployReq.Name,      // deploy名称
			Namespace: deployReq.Namespace, // deploy名称空间
			Labels:    deployReq.Labels,
		},
		Spec: appsv1.DeploymentSpec{ // spec信息
			Replicas: &deployReq.Replicas, // 副本数量
			Selector: &metav1.LabelSelector{ // 标签选择器
				MatchLabels: map[string]string{"app": deployReq.Name},
			},
			Template: corev1.PodTemplateSpec{ // 容器模版信息
				ObjectMeta: metav1.ObjectMeta{
					Labels: map[string]string{"app": deployReq.Name}, // pod标签
				},
				Spec: corev1.PodSpec{
					Containers: []corev1.Container{ // 容器信息
						{
							Name:  deployReq.ContainerName, // 容器名
							Image: deployReq.Image,         // 容器镜像
							Ports: containerPorts,          // 容器端口
							Env:   envVars,                 // 容器环境变量
						},
					},
				},
			},
		},
	}

	// todo 3. 创建 Deployment 资源
	_, err := conf.KuberConfigSet.AppsV1().Deployments(deployReq.Namespace).
		Create(context.TODO(), deployment, metav1.CreateOptions{})
	// 3.1 错误处理
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	// 3.2 返回响应
	ctx.JSON(http.StatusOK, gin.H{
		"message": "创建成功",
		"code":    20000,
	})
}

// DeleteDeploy 删除 Deployment
func (DeploymentsService) DeleteDeploy(ctx *gin.Context) {
	// todo 1. 从 Query Parameters 中获取参数
	name := ctx.Query("name")
	namespace := ctx.Query("namespace")

	// 1.1 如果没传递命名空间，则默认为default
	if namespace == "" {
		namespace = "default" // 设置默认命名空间
	}

	// todo 2. 删除 Deployment
	conf.KuberConfigSet.AppsV1().Deployments(namespace).
		Delete(context.TODO(), name, metav1.DeleteOptions{})

	// todo 3. 传递响应
	ctx.JSON(http.StatusOK, gin.H{
		"code":    20000,
		"message": "删除成功",
	})
}

// GetDeploy 获取指定命名空间下Deployment 列表或详情
func (DeploymentsService) GetDeploy(ctx *gin.Context) {

	// todo 1. 根据命名空间获取deploy详细信息
	// 1.1 获取命名空间的请求参数
	namespace := ctx.DefaultQuery("namespace", "default")

	// 1.2 获取指定命名空间下所有Deployment 详情
	deployments, err := conf.KuberConfigSet.AppsV1().Deployments(namespace).
		List(context.TODO(), metav1.ListOptions{})

	if err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	// todo 2. 将官方对象参数值映射到自定义结构体对象，方便传参
	// 2.1 构建自定义Deployment对象列表
	var deployList []DeploymentInfo
	// 2.2 循环遍历官方对象列表，并进行参数映射
	for _, deploy := range deployments.Items {
		deployInfo := DeploymentInfo{
			Name:              deploy.Name,
			Namespace:         deploy.Namespace,
			Replicas:          *deploy.Spec.Replicas,
			AvailableReplicas: deploy.Status.AvailableReplicas,
			ReadyReplicas:     deploy.Status.ReadyReplicas,
			Labels:            deploy.Labels,
			CreationTimestamp: deploy.CreationTimestamp,
		}

		// 获取所有容器镜像
		var images []string
		for _, container := range deploy.Spec.Template.Spec.Containers {
			images = append(images, container.Image)
		}
		deployInfo.Image = images
		deployList = append(deployList, deployInfo)
	}

	// todo 3. 返回响应
	ctx.JSON(http.StatusOK, gin.H{
		"message": "获取成功",
		"data":    deployList,
		"code":    20000,
	})
}

/*
UpdateDeploy 更新 Deployment
根据命名空间和deploy名称修改副本数或镜像（仅考虑deploy中有一个容器，即只有一个镜像）
*/
func (DeploymentsService) UpdateDeploy(ctx *gin.Context) {
	// todo 1. 获取请求体中参数，并进行参数绑定
	// 1.1 定义自定义deploy更新对象
	var updateReq DeploymentUpdate

	// 1.2 参数绑定、错误处理
	if err := ctx.ShouldBindJSON(&updateReq); err != nil {
		ctx.JSON(http.StatusBadRequest, gin.H{
			"message": "参数错误",
			"error":   err.Error(),
		})
		return
	}

	// todo 2. 更新副本数或镜像
	// 2.1 构建更新数据map映射
	updateData := make(map[string]interface{})
	specData := make(map[string]interface{})

	// 2.2 处理副本数更新
	if updateReq.Replicas != nil {
		specData["replicas"] = *updateReq.Replicas
	}

	// 2.3 处理镜像更新
	if updateReq.Image != nil {
		// 2.3.1 获取当前 Deployment 以获取容器名称
		currentDeploy, err := conf.KuberConfigSet.AppsV1().Deployments(updateReq.Namespace).
			Get(context.TODO(), updateReq.Name, metav1.GetOptions{})
		if err != nil {
			ctx.JSON(http.StatusInternalServerError, gin.H{
				"message": "获取当前 Deployment 失败",
				"error":   err.Error(),
			})
			return
		}
		containerName := currentDeploy.Spec.Template.Spec.Containers[0].Name

		// 2.3.2 构建容器更新结构
		specData["template"] = map[string]interface{}{
			"spec": map[string]interface{}{
				"containers": []map[string]interface{}{
					{
						"name":  containerName,
						"image": *updateReq.Image,
					},
				},
			},
		}
	}

	// 2.4 将 specData 放入 updateData
	if len(specData) > 0 {
		updateData["spec"] = specData
	}

	// todo 3. 更新deploy
	// 3.1 将map结构序列化为JSON
	patchBytes, err := json.Marshal(updateData)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"message": "序列化更新数据失败",
			"error":   err.Error(),
		})
		return
	}

	// 3.2 执行更新
	_, err = conf.KuberConfigSet.AppsV1().Deployments(updateReq.Namespace).
		Patch(context.TODO(), updateReq.Name, types.StrategicMergePatchType, patchBytes, metav1.PatchOptions{})
	if err != nil {
		if errors.IsNotFound(err) {
			ctx.JSON(http.StatusNotFound, gin.H{
				"message": "Deployment 不存在",
			})
			return
		}
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"message": "更新 Deployment 失败",
			"error":   err.Error(),
		})
		return
	}

	// 3.3 返回响应
	ctx.JSON(http.StatusOK, gin.H{
		"code":    20000,
		"message": "更新成功",
	})

}

// GetAllDeploy 获取所有命名空间的 Deployment，按命名空间分组
func (DeploymentsService) GetAllDeploy(ctx *gin.Context) {
	// todo 1. 获取所有命名空间的 Deployment
	deployments, err := conf.KuberConfigSet.AppsV1().Deployments("").
		List(context.TODO(), metav1.ListOptions{})
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	// todo 2. 按命名空间分组,并获取deploy的详细信息
	// 2.1 创建map映射对象，用于保存命名空间和deploy对象
	namespaceDeployments := make(map[string][]DeploymentInfo)

	// 2.2 遍历所用deploy，并进行参数映射
	for _, deploy := range deployments.Items {
		// 2.2.1 获取所有容器镜像
		var images []string
		for _, container := range deploy.Spec.Template.Spec.Containers {
			images = append(images, container.Image)
		}

		// 2.2.2 构建 DeploymentInfo
		deployInfo := DeploymentInfo{
			Name:              deploy.Name,
			Namespace:         deploy.Namespace,
			Replicas:          *deploy.Spec.Replicas,
			AvailableReplicas: deploy.Status.AvailableReplicas,
			ReadyReplicas:     deploy.Status.ReadyReplicas,
			Image:             images,
			Labels:            deploy.Labels,
			CreationTimestamp: deploy.CreationTimestamp,
		}

		// 2.2.3 将按命名空间分组的deploy信息传递给map映射
		namespaceDeployments[deploy.Namespace] = append(namespaceDeployments[deploy.Namespace], deployInfo)
	}

	// todo 3. 返回响应
	ctx.JSON(http.StatusOK, gin.H{
		"code":    20000,
		"message": "获取成功",
		"data":    namespaceDeployments,
	})
}
